﻿using System;
using System.Collections.Generic;
using System.Linq;
using Fuse8.DomainFramework.DataAccess.ExpressionSlicers;
using Fuse8.DomainFramework.Domain;
using Fuse8.DomainFramework.Common;
using Fuse8.DomainFramework.CacheService;
using Fuse8.DomainFramework.Common.Locator;

namespace Fuse8.DomainFramework.DataAccess.InMemoryDatabase
{
    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    /// <typeparam name="TKey"></typeparam>
    internal class InMemoryDataRepository<TEntity, TKey> : BaseEntityDataRepository<TEntity, TKey, TEntity>, IEntityDataRepository<TEntity, TKey>
        where TEntity : class, IEntityObject<TKey>, new()
        where TKey : IComparable<TKey>, IEquatable<TKey> 
    {
        #region Field

        /// <summary>
        /// 
        /// </summary>
        private IList<TEntity> _database = new List<TEntity>();

        #endregion
        
        #region BaseEntityDataRepository methods

        public override DataRepositoryCacheOptions CacheOptions
        {
            get
            {
                return DataRepositoryCacheOptions.NoCache;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        protected override IEntityDataRepositoryMapper<TEntity, TEntity> Mapper
        {
            get
            {
                if (!DependencyServiceLocator.Current.IsDefaultServiceRegistered<IEntityDataRepositoryMapper<TEntity, TEntity>>())
                {
                    DependencyServiceLocator.Current.SetDefaultService<IEntityDataRepositoryMapper<TEntity, TEntity>>(
                        InMemoryDataRepositoryConfiguration<TEntity, TKey>.CreateMapper());
                }

                return base.Mapper;
            }
            set
            {
                base.Mapper = value;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="repositoryEntity"></param>
        protected override void AddToRepository(TEntity repositoryEntity)
        {
            Guard.ArgumentNotNull(repositoryEntity, "repositoryEntity");

            if (repositoryEntity.IsNew)
            {
                repositoryEntity.Id = _database.Count > 0 ?
                    _database.Max(p => p.Id) :
                    default(TKey);

                repositoryEntity.IncreaseId();

                _database.Add(repositoryEntity);
            }
            else if (typeof(TKey) == typeof(Guid))
            {
                _database.Add(repositoryEntity);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="repositoryEntity"></param>
        protected override void SaveToRepository(TEntity repositoryEntity)
        {
            Guard.ArgumentNotNull(repositoryEntity, "repositoryEntity");

            if (repositoryEntity.IsCreated)
            {
                int index = _database.IndexOf(repositoryEntity);
                if (index >= 0)
                {
                    _database[index] = repositoryEntity;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="repositoryEntity"></param>
        protected override void DeleteFromRepository(TEntity repositoryEntity)
        {
            Guard.ArgumentNotNull(repositoryEntity, "repositoryEntity");

            _database.Remove(repositoryEntity);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        protected override void DeleteFromRepository(TKey id)
        {
            var repositoryEntity = FindOne(id);

            if (repositoryEntity != null)
            {
                DeleteFromRepository(repositoryEntity);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        protected override IQueryable<TEntity> AsQueryable()
        {
            var slicer = SlicerHelper.AdaptEpression<TEntity>(null);

            return slicer == null ? _database.AsQueryable() : _database.Where(p => slicer.Compile().Invoke(p)).AsQueryable();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="selectExpression"></param>
        /// <returns></returns>
        public override IQueryable<TEntity> FindAll(System.Linq.Expressions.Expression<Func<TEntity, object>> selectExpression)
        {
            return base.FindAll();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="selectExpression"></param>
        /// <returns></returns>
        public override IQueryable<TEntity> Find(System.Linq.Expressions.Expression<Func<TEntity, bool>> expression, System.Linq.Expressions.Expression<Func<TEntity, object>> selectExpression)
        {
            return base.Find(expression);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="selectExpression"></param>
        /// <returns></returns>
        public override TEntity FindOne(System.Linq.Expressions.Expression<Func<TEntity, bool>> expression, System.Linq.Expressions.Expression<Func<TEntity, object>> selectExpression)
        {
            return base.FindOne(expression);
        }        
        #endregion  
    }
}
